22 juin 2016

Plan

JSON, format document

Notion de document

Pas de réelle définition mais quelques propriétés :

  • un document contient des données encodées dans un certain format,
  • le contenu d'un document est un ensemble de paires clé / valeur,
  • absence de schéma fixe a priori,
  • un document peut contenir d'autres documents,
  • un document peut être enrichi de métadonnées.

Exemples de formats

  • Format texte :
    • JSON
    • XML
    • YAML
  • Format binaire :
    • BSON
    • PDF
{
    "nom": "Skywalker",
    "prenom": "Luke",
    "profession": "Chevalier Jedi"
}
<personnage>
  <nom>Skywalker</nom>
  <prenom>Luke</prenom>
  <profession>Chevalier Jedi</profession>
</personnage>
---
personnage:
    nom:        Skywalker
    prenom:     Luke
    profession: Chevalier Jedi
...

Format JSON

  • Créé par Douglas Crockford entre 2002 et 2005.
  • Format léger très utilisé dans les communications client / serveur.
  • JavaScript Object Notation (mais indépendant du langage).
  • Format ouvert et lisible par l'humain.
  • Décrit par RFC 7159 et ECMA-404.
  • Basé sur des paires clé / valeur.

Exemple de document JSON

Types de base :

  • chaîne de caractères : unicode,
  • nombre : flottants double précision,
  • booléen : true et false,
  • tableau : liste ordonnée d'éléments entre crochets [],
  • null : valeur vide,
  • objet : liste non ordonnée de paires clé / valeur entre accolades {}.
Les caractères blancs (espace, tabulation et retour à la ligne) sont ignorés.
{
    "nom": "Han Solo",
    "taille": 1.85,
    "poids": 80,
    "estCool": true,
    "vaisseaux": [
        "Faucon Millenium",
        "Navette impériale"
    ],
    "maitre": null,
    "enfant": {
        "nom": "Solo",
        "prenom": "Ben"
    }
}

Obtenir du JSON

Package jsonlite

Le package jsonlite est :

  • disponible sur le CRAN,
  • maintenu par Jeroen Ooms,
  • un fork de RJSONIO (mais complétement reécrit depuis),
  • très bien pensé.
library(jsonlite)
articles <- fromJSON("http://starwars.wikia.com/api/v1/Articles/List/?limit=25")

Package jsonlite

L'objet retourné par fromJSON est de type list et ses composants correspondent aux clés du format JSON.

typeof(articles)
## [1] "list"
names(articles)
## [1] "items"    "basepath" "offset"
nrow(articles$items)
## [1] 25

Données JSON / Objet R

Le package jsonlite met (presque) en place une bijection entre la structure des données dans un document JSON et l'objet R retourné par fromJSON.

obj <- list(nom="Dark Vador", cote="Obscur")
toJSON(obj)
## {"nom":["Dark Vador"],"cote":["Obscur"]}
all.equal(obj, fromJSON(toJSON(obj)))
## [1] TRUE

Données JSON / Objet R

obj <- data.frame(
  nom=c("Luke", "Leia", "Dark Vador"),
  taille=c(1.72, 1.50, 2.02),
  obscur=c(FALSE, FALSE, TRUE),
  stringsAsFactors=FALSE)

all.equal(obj, fromJSON(toJSON(obj)))
## [1] TRUE
toJSON(obj, pretty=TRUE)
## [
##   {
##     "nom": "Luke",
##     "taille": 1.72,
##     "obscur": false
##   },
##   {
##     "nom": "Leia",
##     "taille": 1.5,
##     "obscur": false
##   },
##   {
##     "nom": "Dark Vador",
##     "taille": 2.02,
##     "obscur": true
##   }
## ]

Données JSON / Objet R

La "presque" bijection se cache dans des détails comme la non prise en charge de NaN ou Inf dans certains cas.

x <- data.frame(a=c(3.14, NaN, Inf))
fromJSON(toJSON(x))
##      a
## 1 3.14
## 2   NA
## 3   NA
x <- list(a=c(3.14, NaN, Inf))
fromJSON(toJSON(x))
## $a
## [1] 3.14  NaN  Inf

Données JSON / Objet R

Les valeurs manquantes ne sont tout simplement pas stockées.

x <- data.frame(star=c(FALSE,  TRUE, NA, NA),
                wars=c("Jedi", NA,   NA, "Galaxy"))
toJSON(x)
## [{"star":false,"wars":"Jedi"},{"star":true},{},{"wars":"Galaxy"}]

Pour une discussion plus poussée sur cette "bijection" et de ses limites :

Jeroen Ooms, The jsonlite Package: A Practical and Consistent Mapping Between JSON Data and R Objects, 2014

https://arxiv.org/abs/1403.2805

Regrouper des données

La plupart du temps, les API limitent le nombre de réponses par requête et imposent l'utilisation de pages.

url <- "http://starwars.wikia.com/api/v1/Search/List/?query=r2d2&limit=10"
pages <- list()
for (i in 1:5) {
  data_page <- fromJSON(paste0(url, "&batch=", i))
  pages[[i]] <- data_page$items
}
items <- rbind.pages(pages)
nrow(items)
## [1] 50

Flux de données

Pour manipuler de grands volumes de données sous forme de flux, le package jsonlite propose les fonctions stream_in et stream_out. Celles-ci permettent de manipuler des données au format JSON "ligne par ligne".

stream_out(iris, stdout())
stream_out(iris, file("/tmp/iris.json"), verbose=FALSE)

Flux de données

stream_in(file("/tmp/iris.json"), pagesize=42, verbose=FALSE, handler=function(df) {
            # Traitement des données
            df <- df[df$Species == "virginica",]
            message(nrow(df), " ligne(s) conservée(s)")
            stream_out(df, file("/tmp/iris2.json"), verbose=FALSE)
          })
## 0 ligne(s) conservée(s)
## 0 ligne(s) conservée(s)
## 26 ligne(s) conservée(s)
## 24 ligne(s) conservée(s)

Plus d'infos et d'exemples dans la doc…

T'entraîner seul,
maintenant, tu dois.


MongoDB, base de données orientée documents

Bases de données orientées documents

  • Programmes assurant le stockage et la gestion de données dans un format document (JSON, par exemple).
  • Généralement, ces bases de données supportent les opérations standards, dites CRUD :
    • Creation (insertion de données)
    • Retrieval (récupération de données : recherche, filtre,…)
    • Update (modification de données)
    • Deletion (suppression de données)
  • L'organisation des documents peut prendre différentes formes (collections, tags, arborescence de dossiers,…)

Bases de données orientées documents


Et bien d'autres…

NoSQL

  • "Non SQL" ou "Not Only SQL", idée déjà présente dans les années 60, i.e. avant l'épisode IV - A New Hope, remise au goût du jour dans les années 2000.
  • Pas de définition claire, tout modèle de base de données abandonnant une partie du modèle relationnel peut être désigné de NoSQL.
  • Souvent, la contrainte de consistence (i.e. même données partout) est relâchée au profit d'autres propriétés.
  • Plusieurs types (orientés colonnes, graphes,…) dont les bases de données orientées documents.

Mongo DB

  • Logiciel libre sous double licences (GNU AGPL v3.0 et Apache 2).
  • Serveur auquel il faut se connecter avec un client.
  • Réputé facile d'utilisation, c'est une solution très populaire :
    http://db-engines.com/en/ranking
  • La langue maternelle de mongoDB est le JavaScript (exécution de fonctions côté serveur).

Mongo DB

  • Format JSON (en fait, c'est BSON…)
  • Documents enrichis d'un champ _id et organisés en collections.
  • Aspect relationnel au travers de l'emboîtement des documents.
  • Absence de schéma : pour créer un document (ou une collection de documents), il suffit de l'utiliser… une faute de frappe peut avoir des conséquences importantes!

Afin d'interagir avec MongoDB depuis R, nous utiliserons le package mongolite qui dépend de jsonlite (même auteur, Jeroen Ooms) comme la Force dépend des midi-chloriens.

"Without the midi-chlorians, life could not exist, and we would have no knowledge of the Force."
― Qui-Gon Jinn

Mongo DB

  • Format JSON (en fait, c'est BSON…)
  • Documents enrichis d'un champ _id et organisés en collections.
  • Aspect relationnel au travers de l'emboîtement des documents.
  • Absence de schéma : pour créer un document (ou une collection de documents), il suffit de l'utiliser… une faute de frappe peut avoir des conséquences importantes!

Afin d'interagir avec mongoDB depuis R, nous utiliserons le package mongolite qui dépend de jsonlite (même auteur, Jeroen Ooms) comme la Force dépend des midi-chloriens.

"Without the midi-chlorians, life could not exist, and we would have no knowledge of the Force."
― Qui-Gon Jinn

Connexion

La fonction mongo permet de se connecter au serveur mongoDB (par défaut, localhost) et de récupérer un objet R pour interagir avec une collection donnée.

library(mongolite)
col_people <- mongo(collection="people", verbose=FALSE)

La collection n'a pas besoin d'être préalablement créée et l'objet retourné par mongo offre plusieurs méthodes pour la manipuler. Par exemple, count sans paramètre retourne le nombre de documents.

col_people$count()
## [1] 0

Insertion

La méthode insert permet d'insérer un data frame dans la collection.

obj <- fromJSON("http://swapi.co/api/people/")
col_people$insert(obj$results)

L'absence de schéma permet d'insérer des data frames qui n'ont pas la même structure.

col_people$insert(data.frame(name="Captain Kirk", alias="Kirky"))
col_people$count()
## [1] 11

Recherche

  • La méthode find retourne les données correpondantes à certains critères passés au paramètre query.
  • Sans paramètre, toute la collection est retournée.
  • Le format JSON est utilisé pour les paramètres.
col_people$find()
col_people$find(query='{"gender":"male"}')

Pour limiter les champs retournés, on utilisera le paramètre fields.

col_people$find(query='{"gender":"male"}', fields='{"name":1}')

Par défaut, le champ _id est toujours retourné…

Recherche

col_people$find(query='{"gender":"male"}', fields='{"_id":0, "name":1}')
##                name
## 1    Luke Skywalker
## 2       Darth Vader
## 3         Owen Lars
## 4 Biggs Darklighter
## 5    Obi-Wan Kenobi

Recherche

Il est possible d'utiliser des commandes dans le paramètre query. Celles-ci sont précédées du caractère '$'. Par exemple, $exists pour tester l'existence d'un champ :

col_people$find(query='{"gender": {"$exists":false} }', fields='{"_id":0, "name":1}')

La commande $regex permet d'utiliser des expressions régulières :

col_people$find(query='{"name": {"$regex":"^C"} }',
                fields='{"_id":0, "name":1, "alias":1}')
##           name alias
## 1        C-3PO  <NA>
## 2 Captain Kirk Kirky

Tri

Le paramètre sort de la méthode find permet de trier les résultats selon les valeurs d'un champ (1 pour l'ordre croissant et -1 pour le décroissant).

col_people$find(fields='{"_id":0, "name":1}', sort='{"name":1}')
col_people$find(fields='{"_id":0, "name":1, "height":1}', sort='{"height":-1}')

Tri

df <- col_people$find(fields='{"_id":0, "name":1, "height":1}', sort='{"height":-1}')
print(df)
##                  name height
## 1               R5-D4     97
## 2               R2-D2     96
## 3         Darth Vader    202
## 4   Biggs Darklighter    183
## 5      Obi-Wan Kenobi    182
## 6           Owen Lars    178
## 7      Luke Skywalker    172
## 8               C-3PO    167
## 9  Beru Whitesun lars    165
## 10        Leia Organa    150
## 11       Captain Kirk   <NA>

Tri

df <- col_people$find(fields='{"_id":0, "name":1, "height":1}', sort='{"height":-1}')
print(df)
##                  name height
## 1               R5-D4     97
## 2               R2-D2     96
## 3         Darth Vader    202
## 4   Biggs Darklighter    183
## 5      Obi-Wan Kenobi    182
## 6           Owen Lars    178
## 7      Luke Skywalker    172
## 8               C-3PO    167
## 9  Beru Whitesun lars    165
## 10        Leia Organa    150
## 11       Captain Kirk   <NA>
str(df)
## 'data.frame':    11 obs. of  2 variables:
##  $ name  : chr  "R5-D4" "R2-D2" "Darth Vader" "Biggs Darklighter" ...
##  $ height: chr  "97" "96" "202" "183" ...

Suppression

La méthode remove peut être utilisée pour :

  • supprimer une entrée correspondant à une recherche donnée,
col_people$remove(query='{"gender":"male"}')
col_people$find()
  • supprimer toutes les entrées correspondantes à une recherche donnée,
col_people$remove(query='{"gender":"male"}', multiple=TRUE)
col_people$find()

La méthode drop permet de supprimer toute une collection.

col_people$drop()
col_people$find(); col_people$count()

On reprend proprement…

clean_df <- function(df) {
  df$height <- suppressWarnings(as.double(df$height))
  df$mass <- suppressWarnings(as.double(gsub(",", "", df$mass)))
  df$films <- lapply(df$films,
                     function(films) { sort(as.integer(substr(films, 27, 27))) })
  return(df)
}

Toutes les pages peuvent être récupérées (la connexion échoue parfois…)

i <- 0
repeat {
  i <- i + 1
  content <- fromJSON(paste0("http://swapi.co/api/people/?page=", i))
  col_people$insert(clean_df(content$results))
  if (is.null(content$`next`)) break
}

Import / Export

  • La méthode export permet d'exporter une collection vers un fichier JSON.
col_people$export(file("/tmp/people.json"))
  • La méthode import permet d'importer une collection (utile pour les sauvegardes de petites collections ;-)
col_people$drop()
col_people$count()
col_people$import(file("/tmp/people.json"))
col_people$count()

Return of the Jedi

Le tri décroissant sur height est maintenant correct.

col_people$find(fields='{"_id":0, "name":1, "height":1}', sort='{"height":-1}')

Le paramètre limit permet de ne récupérer que les premiers éléments d'une réponse.

col_people$find(fields='{"_id":0, "name":1, "height":1}', sort='{"height":-1}', limit=5)
##           name height
## 1  Yarael Poof    264
## 2      Tarfful    234
## 3      Lama Su    229
## 4    Chewbacca    228
## 5 Roos Tarpals    224

Quelques opérateurs

  • Comparaison $lt, $lte, $gt et $gte
col_people$find(query='{"height": {"$gt":160} }',
                fields='{"_id":0, "name":1, "height":1}')
  • Non-égalité $ne
col_people$find(query='{"gender": {"$ne": "male"} }',
                fields='{"_id":0, "name":1, "gender":1}')
  • Opérateurs logiques $and, $or, $nor et $not
col_people$find(query='{"$or": [{"gender" : "male"}, {"gender" : "female"}]}',
                fields='{"_id":0, "name":1, "gender":1}')

Quelques opérateurs

  • (Non-)inclusion $in et $nin
# Les films sont dans l'ordre historique
ffilms <- '{"_id":0, "name":1, "films":1}'
col_people$find(query='{"films": {"$in": [1,2,3]} }', fields=ffilms)
col_people$find(query='{"films": {"$nin": [1,2,3]} }', fields=ffilms)
  • Inclusion stricte $all
col_people$find(query='{"films": {"$all": [1,2,3]} }', fields=ffilms)
  • Taille d'un tableau $size
col_people$find(query='{"films": {"$size":3} }', fields=ffilms)
  • Et bien d'autres ($type, $mod, $elemMatch,…)

Itérateurs

La méthode iterate prend les mêmes paramètres que find et retourne un itérateur pour parcourir le résultat de la recherche ligne par ligne à l'aide de $one (ou par page avec $page).

Une fois le dernier élément retourné, l'itérateur devient obsolète.

it <- col_people$iterate(
  query='{"$and": [
    {"height": {"$gt":160} }, {"mass": {"$lt":50} }
  ]}',
  fields='{"_id":0, "name":1}',
  sort='{"name":1}'
)

repeat {
  item <- it$one()
  if (!is.null(item)) message("Force et honneur, ", item$name, "!") else break
}
## Force et honneur, Padmé Amidala!
## Force et honneur, Sly Moore!
## Force et honneur, Wat Tambor!

Modification

Pour modifier un document de la collection, la méthode update procède en deux temps :

  • rechercher le(s) document(s) à modifier,
  • apporter les modifications au(x) document(s).

La recherche se fait à l'aide du paramètre query et les modifications avec update. Plusieurs commandes sont disponibles pour modifier le(s) document(s).

  • Changer la valeur d'une clé $set
col_people$update(query='{"name":"Luke Skywalker"}',
                  update='{"$set": {"hair_color":"blond, grey"} }')
col_people$find(query='{"name":"Luke Skywalker"}',
                fields='{"_id":0, "name":1, "hair_color":1}')

Modification

  • Ajouter une valeur dans un tableau $push
col_people$update(query='{"name":"Luke Skywalker"}',
                  update='{"$push": {"films":8} }') # Spoiler !
col_people$find(query='{"name":"Luke Skywalker"}', fields=ffilms)
  • Ajouter une valeur dans un tableau sans doublon $addToSet
col_people$update(query='{"name":"Luke Skywalker"}',
                  update='{"$addToSet": {"films":8} }')
col_people$find(query='{"name":"Luke Skywalker"}', fields=ffilms)
  • Retirer une valeur dans un tableau $pull
col_people$update(query='{"name":"Luke Skywalker"}',
                  update='{"$pull": {"films":8} }')
col_people$find(query='{"name":"Luke Skywalker"}', fields=ffilms)

Modification

Comme d'habitude, attention aux fautes de frappes!

col_people$update(query='{"name":"Luke Skywalker"}',
                  update='{"$set": {"hair_colour":"blond, grey"} }')
col_people$find(query='{"name":"Luke Skywalker"}')
  • Supprimer une paire clé / valeur $unset
col_people$update(query='{"name":"Luke Skywalker"}',
                  update='{"$unset": {"hair_colour": "blond, grey"} }')
col_people$find(query='{"name":"Luke Skywalker"}')
  • Et bien d'autres ($inc, $pop, $pushAll, $pullAll,…)

  • Pour appliquer les modifications à plusieurs documents, utiliser multiple=TRUE.

T'entraîner seul,
maintenant, tu dois.


Un (petit) peu de statistique

Return of Count (Dooku)

La méthode count permet de compter des documents. Bref, la base…

pie(c(col_people$count('{"gender":"male"}'),
      col_people$count('{"gender":"female"}'),
      col_people$count('{"gender": {"$nin":["male","female"]} }')),
    labels=c("Masculin", "Féminin", "Autres"), radius=1)

Valeurs uniques

La méthode distinct retourne la liste des valeurs prises par une clé parmi les documents vérifiant une recherche donnée.

col_people$distinct(key="gender")
## [1] "male"          "n/a"           "female"        "hermaphrodite"
## [5] "none"
col_people$distinct(key="hair_color", query='{"gender":"female"}')
## [1] "brown"   "auburn"  "black"   "none"    "blonde"  "white"   "unknown"

Table de contingence

Comment compter les effectifs de différents groupes?

values <- col_people$distinct(key="gender"); count <- NULL
for (v in values) {
  count <- c(count, col_people$count(paste0('{"gender":"', v, '"}')))
}
names(count) <- values; print(count)
##          male           n/a        female hermaphrodite          none 
##            62             3            19             1             2

Table de contingence

Comment compter les effectifs de différents groupes?

values <- col_people$distinct(key="gender"); count <- NULL
for (v in values) {
  count <- c(count, col_people$count(paste0('{"gender":"', v, '"}')))
}
names(count) <- values; print(count)
##          male           n/a        female hermaphrodite          none 
##            62             3            19             1             2
  • Difficile à relire.
  • Difficile à généraliser.
  • Beaucoup d'aller-retours entre le client et le serveur.

Table de contingence

Comment compter les effectifs de différents groupes?

table(col_people$find(fields='{"_id":0,"gender":1}'))
## 
##        female hermaphrodite          male           n/a          none 
##            19             1            62             3             2

Table de contingence

Comment compter les effectifs de différents groupes?

table(col_people$find(fields='{"_id":0,"gender":1}'))
## 
##        female hermaphrodite          male           n/a          none 
##            19             1            62             3             2
  • Facile à relire.
  • Facile à généraliser.
  • Une seule requête mais elle retourne un objet potentiellement volumineux.
  • Tous les calculs sont faits côté client.

Agrégateurs

Un agrégateur est une fonction qui regroupe les valeurs contenues dans plusieurs documents sélectionnés et retourne une structure contenant des objets "simples" et "plus informatifs".

Agrégateurs

Les méthodes count et distinct sont des agrégateurs.

Agrégateurs

Les méthodes count et distinct sont des agrégateurs.

Pipeline d'agrégation

  • Modèle d'agrégation privilégié de MongoDB.
  • Procède par étapes pour transformer progressivement les documents en résultat agrégé.
  • Les étapes filtrent, transforment, groupent, trient,… les documents dans un ordre établi dont voici un aperçu,
    • $match (filtre comme avec query),
    • $group (regroupe et accumule),
    • $sort (trie).
  • Pour les autres étapes ($project, $limit, $skip, $sample, $out,…), voir :

https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/

Pipeline d'agrégation

La méthode aggregate permet de contruire un agrégateur basé sur le modèle du pipeline d'agrégation. Cette méthode prend un tableau en paramètre contenant les différentes étapes à réaliser.

Pipeline d'agrégation

La méthode aggregate permet de contruire un agrégateur basé sur le modèle du pipeline d'agrégation. Cette méthode prend un tableau en paramètre contenant les différentes étapes à réaliser.

Exemple pour count :

col_people$aggregate('
  [
    { "$match": { "hair_color": "white" } },
    { "$group": { "_id": null, "count": { "$sum": 1 } } }
  ]')
##   _id count
## 1  NA     4

Pipeline d'agrégation

col_people$aggregate('
  [
    { "$match": { "hair_color": "white" } },
    { "$group": { "_id": null, "count": { "$sum": 1 } } }
  ]')
  • $match est similaire à query,
  • le champ _id de $group reçoit la clé utilisée pour les groupes ou null pour considérer tous les documents,
  • le champ count de $group est le nom de l'accumulateur défini en suivant,
  • la valeur des accumulateurs est maintenue groupe par groupe,
  • la définition des accumulateurs est évaluée pour chaque document.

Pipeline d'agrégation

Exemple pour distinct :

col_people$aggregate('
  [
    { "$group": { "_id": "$gender" } }
  ]')

ou bien :

col_people$aggregate('
  [
    { "$group": { "_id": null, "gender": { "$addToSet": "$gender" } } }
  ]')

Pipeline d'agrégation

Exemple pour compter des effectifs et les trier :

col_people$aggregate('
  [
    { "$group": { "_id": "$gender", "count": { "$sum": 1 } } },
    { "$sort": { "count": -1 } }
  ]')
##             _id count
## 1          male    62
## 2        female    19
## 3           n/a     3
## 4          none     2
## 5 hermaphrodite     1

Pipeline d'agrégation

Exemple un peu plus avancé :

col_people$aggregate('
  [{ "$group": { "_id": "$gender",
                 "height": { "$avg": "$height" },
                 "mass": { "$avg": "$mass" } }
  }]')
##             _id   height       mass
## 1          none 200.0000  140.00000
## 2 hermaphrodite 175.0000 1358.00000
## 3        female 165.4706   54.02000
## 4           n/a 120.0000   46.33333
## 5          male 179.2373   81.00455

Pipeline d'agrégation

Limites du pipeline

  • Chaque document de la collection résultante est limité à 16MB (contrainte liée au format BSON).
  • Chaque étape est limitée à 100MB de RAM ce qui peut poser des problèmes pour les gros jeux de données.
  • Le pipeline est limité aux opérateurs définis par MongoDB et ne permet pas d'utiliser des fonctions plus "souples".

Map-Reduce

MongoDB propose une autre méthode, appelée Map-Reduce, pour réaliser des agrégations.

  • Map-Reduce opère en deux étapes :
    • map : une fonction est appliquée à chaque document et elle "émet" une (ou plusieurs) paire(s) clé / valeur,
    • reduce : les résultats des appels map sont regroupés pour produire le résultat final.
  • La méthode à utiliser s'appelle mapreduce.
  • Il est possible de limiter Map-Reduce au résultat d'une recherche avec le paramètre query, de trier le résultat avec sort et de le limiter avec limit.
  • Les fonctions map et reduce sont écrites en JavaScript.

Map-Reduce

MongoDB propose une autre méthode, appelée Map-Reduce, pour réaliser des agrégations.



Map-Reduce offre plus de souplesse que le pipeline d'agrégation mais ce dernier doit rester le choix à privilégier car Map-Reduce est moins efficace et plus complexe en général.

Map-Reduce

Compter des effectifs avec Map-Reduce :

col_people$mapreduce(
  'function() { emit(this.gender, 1) }',
  'function(key, values) { return Array.sum(values) }'
)
##             _id value
## 1        female    19
## 2 hermaphrodite     1
## 3          male    62
## 4           n/a     3
## 5          none     2

Map-Reduce

Mesurer la longueur moyenne des noms en fonction du genre :

col_people$mapreduce(
  'function() { emit(this.gender, this.name.length) }',
  'function(key, values) { return Array.sum(values) / values.length }'
)
##             _id    value
## 1        female 10.10526
## 2 hermaphrodite 21.00000
## 3          male 10.62903
## 4           n/a  5.00000
## 5          none  4.00000

Que l'histogramme soit avec toi…

height_range <- col_people$aggregate('[
  { "$group": { "_id": null,
                "min": { "$min": "$height" },
                "max": { "$max": "$height" } }
  }
]')

n <- 5; step <- (height_range$max - height_range$min) / n

mydata <- col_people$mapreduce(
  paste0('function() {
    var cat = Math.floor((this.height - ', height_range$min, ')/', step, ');
    emit("Cat" + cat.toString(), 1)
  }'),
  'function(key, values) { return Array.sum(values) }'
)

Que l'histogramme soit avec toi…

val <- mydata$value[1:n]
val[n] <- val[n] + mydata$value[n + 1]
barplot(val, col="yellow", space=0)
axis(1, at=0:n, labels=height_range$min+(0:n)*step)

T'entraîner seul,
maintenant, tu dois.



Merci à toutes et à tous !

Bonnes Rencontres R 2016 !